package com.k8.common.aop;

import com.k8.common.aop.internal.advisor.Advisor;
import com.k8.common.aop.internal.exception.JdkAspectException;
import com.k8.common.util.AopUtil;
import com.k8.rpc.url.URL;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;

//jdk
public class JdkProxyFactory implements ProxyFactory {
    @Override
    public Object build(URL url, Object target, List<Advisor> cacheAdvisors) {
        return build(target, cacheAdvisors);
    }

    @Override
    public <T> T createInterfaceProxy(Class<T> clazz, InterfaceProxyMethodInterceptor interfaceProxyMethodInterceptor) {
        return createInterfaceProxy(clazz, determineClassLoader(clazz.getClassLoader()), interfaceProxyMethodInterceptor);
    }

    @Override
    public <T> T createInterfaceProxy(Class<T> clazz, ClassLoader classLoader, InterfaceProxyMethodInterceptor interceptor) {
        if (!clazz.isInterface()) throw new RuntimeException("Class is not a interface , class: " + clazz.getName());
        SimpleInvocationHandler simpleInvocationHandler = new SimpleInvocationHandler(interceptor);
        return (T) Proxy.newProxyInstance(classLoader, new Class[]{clazz}, simpleInvocationHandler);
    }


    private ClassLoader determineClassLoader(ClassLoader classLoader) {
        if (classLoader == null) {
            return this.getClass().getClassLoader();
        } else {
            if (classLoader.getParent() == null) {
                ClassLoader aopClassLoader = this.getClass().getClassLoader();
                for (ClassLoader aopParent = aopClassLoader.getParent(); aopParent != null; aopParent = aopParent.getParent()) {
                    if (classLoader == aopParent) {
                        return aopClassLoader;
                    }
                }
            }
            return classLoader;
        }
    }

    @Override
    public Object build(Object target, List<Advisor> cacheAdvisors) {
        boolean proxyNecessary = AdvisedSupport.isProxyNecessary(target, cacheAdvisors);
        if (!proxyNecessary) {
            AdvisedSupport.cacheCandidates.remove();
            return target;
        }
        AdvisedSupport advisedSupport = null;
        try {
            advisedSupport = new JdkAdvisedSupport(target);
            JdkAopProxy jdkAopProxy = new JdkAopProxy(advisedSupport);
            return jdkAopProxy.getProxy();
        } catch (JdkAspectException e) {
            //todo 这里应该切换成使用javassist或cglib等基于继承的方式进行动态代理，而不是抛出异常
            throw new RuntimeException(e);
        }
    }

    public static class SimpleInvocationHandler implements InvocationHandler {
        private final InterfaceProxyMethodInterceptor interceptor;

        public SimpleInvocationHandler(InterfaceProxyMethodInterceptor interceptor) {
            this.interceptor = interceptor;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (AopUtil.isNoOpMethod(method)) {
                return method.invoke(args);
            }
            return interceptor.process(proxy, method, args);
        }
    }
}
